<template>
  <div class="fx-image">
    <img :src="src" :alt="$attrs.alt" :class="{background:isBackground}" :style="imageStyle" ref="imageDom"
         draggable="false" @click="showPreviewClick">
  </div>
  <transition @enter="previewEnter"
              @before-enter="previewBeforeEnter"
              @before-leave="previewBeforeLeave"
              @leave="previewLeave">
    <div class="fx-image-preview-list" v-if="showPreview&&previewList&&previewList.length" :style="previewListStyle">
      <div class="close" @click="closeClick" v-if="showPreview"><i class="iconfont icon-multiply fs20"/></div>
      <div class="prev" @click="prevClick" v-if="showPreview"><i class="iconfont icon-left fs12"/></div>
      <img :src="previewList[previewIndex]" :alt="previewList[previewIndex]" :style="previewImageStyle"
           ref="previewImageDom" draggable="false" @mousedown="imageMousedown">
      <div class="next" @click="nextClick" v-if="showPreview"><i class="iconfont icon-right fs12"/></div>
      <ul class="fx-preview-controller" v-if="showPreview">
        <li><i class="iconfont icon-reduce fs16" @click="zoomClick('reduce')"/></li>
        <li><i class="iconfont icon-enlarge fs16" @click="zoomClick('enlarge')"/></li>
        <li><i class="iconfont icon-rotate-left fs16" @click="rotateClick('left')"/></li>
        <li><i class="iconfont icon-rotate-right fs16" @click="rotateClick('right')"/></li>
      </ul>
    </div>
  </transition>
</template>

<script lang="ts">
import { computed, defineComponent, onBeforeUnmount, onMounted, PropType, reactive, ref } from 'vue'
import { codeRule } from '@/base/types'
import { getZIndex } from '@/base/hooks'
import { prefixStyle } from '@/base/utils'

export interface ImageProps {
  src: string;
  width: string | number;
  height: string | number;
  fit: string;
  previewList: string[];
  isBackground: boolean;
  background: string;
}

export default defineComponent({
  name: 'fxImage',
  props: {
    // 图片地址
    src: {
      type: String as PropType<string>,
      required: true
    },
    // 宽度
    width: {
      type: [String, Number] as PropType<string | number>,
      default: null,
      validator (val: string | number) {
        if (typeof val === 'string') return codeRule.test(val)
        return true
      }
    },
    // 宽度
    height: {
      type: [String, Number] as PropType<string | number>,
      default: null,
      validator (val: string | number) {
        if (typeof val === 'string') return codeRule.test(val)
        return true
      }
    },
    // 图片平铺方式
    fit: {
      type: String as PropType<string>,
      default: 'contain'
    },
    // 预览图
    previewList: {
      type: Array as PropType<string[]>,
      default: null
    },
    // 是否背景色
    isBackground: {
      type: Boolean as PropType<boolean>,
      default: false
    },
    // 背景色
    background: {
      type: String as PropType<string>,
      default: null
    }
  },
  setup (props: ImageProps) {
    // 图片DOM
    const imageDom = ref<HTMLElement>()
    // 预览图片DOM
    const previewImageDom = ref<HTMLElement>()
    // 展示预览列表
    const showPreview = ref<boolean>(false)
    // 当前缩放大小
    const currentScale = ref<number>(1)
    // 当前旋转角度
    const currentRotate = ref<number>(0)
    // 预览索引
    const previewIndex = ref<number>(0)
    // 预览拖动数据
    const previewInfo = ref<{ disX: number; disY: number; left: number; top: number; start: boolean }>({
      disX: 0,
      disY: 0,
      left: 0,
      top: 0,
      start: false
    })
    // 样式
    const imageStyle = computed(() => {
      const isNumWidth = typeof props.width === 'number'
      const isNumHeight = typeof props.height === 'number'
      const isUnitWidth = codeRule.test(props.width as string)
      const isUnitHeight = codeRule.test(props.height as string)
      return {
        width: isNumWidth ? `${props.width}px` : isUnitWidth ? props.width : null,
        height: isNumHeight ? `${props.height}px` : isUnitHeight ? props.width : null,
        objectFit: props.fit,
        backgroundColor: props.isBackground && props.background ? props.background : null
      }
    })
    // 预览列表
    const previewListStyle = computed(() => {
      return {
        zIndex: getZIndex()
      }
    })
    // 预览样式
    const previewImageStyle = computed(() => {
      const Transform = prefixStyle('transform')
      const style: { [key: string]: string } = {
        left: `${previewInfo.value.left}px`,
        top: `${previewInfo.value.top}px`
      }
      style[Transform] = `scale(${currentScale.value}) rotate(${currentRotate.value}deg)`
      return style
    })
    // 动画数据
    const previewAnimation = reactive<{ left: number; top: number; width: number; height: number }>({
      left: 0,
      top: 0,
      width: 0,
      height: 0
    })

    // 预览点击事件
    const showPreviewClick = (ev: MouseEvent): void => {
      const target = ev.target as HTMLElement
      previewAnimation.left = target.clientLeft
      previewAnimation.top = target.clientTop
      previewAnimation.width = target.clientWidth
      previewAnimation.height = target.clientHeight
      setTimeout(() => {
        showPreview.value = true
      }, 100)
    }
    // 上一页
    const prevClick = () => {
      if (!props.previewList?.length) return
      clearPreviewInfo()
      previewIndex.value--
      if (previewIndex.value < 0) previewIndex.value = props.previewList.length - 1
    }
    // 下一页
    const nextClick = () => {
      if (!props.previewList?.length) return
      clearPreviewInfo()
      previewIndex.value++
      if (previewIndex.value > props.previewList.length - 1) previewIndex.value = 0
    }
    // 拖动
    const imageMousedown = (ev: MouseEvent) => {
      previewInfo.value.start = true
      previewInfo.value.disX = ev.clientX - previewInfo.value.left
      previewInfo.value.disY = ev.clientY - previewInfo.value.top
      document.addEventListener('mousemove', imageMousemove)
      document.addEventListener('mouseup', imageMouseup)
    }
    // 拖动移动
    const imageMousemove = (ev: MouseEvent) => {
      if (!previewInfo.value.start) return
      previewInfo.value.left = ev.clientX - previewInfo.value.disX
      previewInfo.value.top = ev.clientY - previewInfo.value.disY
    }
    // 拖动结束
    const imageMouseup = () => {
      previewInfo.value.start = false
      document.removeEventListener('mousemove', imageMousemove)
      document.removeEventListener('mouseup', imageMouseup)
    }
    // 清空移动状态
    const clearPreviewInfo = () => {
      previewInfo.value.left = 0
      previewInfo.value.top = 0
      previewInfo.value.disX = 0
      previewInfo.value.disY = 0
      previewInfo.value.start = false
      currentScale.value = 1
      currentRotate.value = 0
    }
    // 关闭预览
    const handleClose = (ev: KeyboardEvent) => {
      ev.code === 'Escape' && closeClick()
    }
    // 缩放点击
    const zoomClick = (zoom: 'reduce' | 'enlarge') => {
      if (zoom === 'reduce') {
        currentScale.value -= 0.1
        if (currentScale.value <= 0.1) currentScale.value = 0.1
      } else if (zoom === 'enlarge') {
        currentScale.value += 0.1
      }
    }
    // 旋转点击
    const rotateClick = (rotate: 'left' | 'right') => {
      if (rotate === 'left') {
        currentRotate.value -= 90
      } else if (rotate === 'right') {
        currentRotate.value += 90
      }
    }
    // 关闭点击
    const closeClick = () => {
      clearPreviewInfo()
      previewIndex.value = 0
      showPreview.value = false
    }
    // 预览进入
    const previewEnter = (el: HTMLElement) => {
      setTimeout(() => {
        el.style.left = '0px'
        el.style.top = '0px'
        el.style.width = `${document.documentElement.clientWidth}px`
        el.style.height = `${document.documentElement.clientHeight}px`
        el.style.backgroundColor = 'rgba(0, 0, 0, .5)'
      })
    }
    // 预览进入前
    const previewBeforeEnter = (el: HTMLElement) => {
      if (!imageDom.value) return
      const rect = imageDom.value?.getBoundingClientRect()
      el.style.left = `${rect.left}px`
      el.style.top = `${rect.top}px`
      el.style.width = `${previewAnimation.width}px`
      el.style.height = `${previewAnimation.height}px`
      el.style.transition = 'all 0.3s, background-color 0.3s 0.2s'
    }
    // 预览进入前
    const previewBeforeLeave = (el: HTMLElement) => {
      el.style.backgroundColor = 'rgba(0, 0, 0, 0)'
      el.style.transition = 'all 0.3s 0.1s, background-color 0.1s'
    }
    // 预览进入前
    const previewLeave = (el: HTMLElement) => {
      if (!imageDom.value) return
      const rect = imageDom.value?.getBoundingClientRect()
      el.style.left = `${rect.left}px`
      el.style.top = `${rect.top}px`
      el.style.width = `${previewAnimation.width}px`
      el.style.height = `${previewAnimation.height}px`
    }

    onMounted(() => {
      document.addEventListener('keydown', handleClose)
    })
    onBeforeUnmount(() => {
      document.removeEventListener('keydown', handleClose)
    })

    return {
      imageDom,
      previewImageDom,
      showPreview,
      previewIndex,
      imageStyle,
      previewListStyle,
      previewImageStyle,
      showPreviewClick,
      prevClick,
      nextClick,
      zoomClick,
      closeClick,
      rotateClick,
      imageMousedown,
      previewEnter,
      previewBeforeEnter,
      previewBeforeLeave,
      previewLeave
    }
  }
})
</script>
